/* eslint-disable no-shadow */
/*
 * @Description:过滤条件类
 *            详见手册API说明-过滤表达式约定
 * @Version: 2.0
 * @Autor: wiz
 * @Date: 2022-12-09 14:25:15
 * @LastEditors: wiz
 * @LastEditTime: 2023-07-17 11:24:22
 */
import { I_fv } from './fv';

// 逻辑枚举类型
export enum V_filterLogical {
  AND = 100, // 与
  OR, // 或
  NOR, // 或非
  NOT // 非
}
// 比较枚举类型
export enum V_filterComparison {
  EQ, // 等于
  NE, // 不等于
  GT, // 大于
  LT, // 小于
  GTE, // 大于等于
  LTE, // 小于等于
  IN, // 包含
  NIN // 不包含
}

export class T_filter {
  // 保存过滤结果
  private _filter: object;

  constructor() {
    this._filter = {};
  }

  public get filter(): object {
    return this._filter;
  }

  public set filter(f: object) {
    this._filter = f;
  }

  // 清空filter
  public clearFilter() {
    this._filter = {};
  }

  // 格式化
  private formatValue(v: any): any {
    return v;
  }

  // 将一下field value 数组格式化成{$and:[{f1:{$eq:v1}}...]}
  public parseWithFieldValue(p: I_fv[]): object {
    const fs: object[] = [];
    p.forEach(ele => {
      const v = this.parseEQ(ele.field, ele.value);
      Object.keys(v).length > 0 && fs.push(v);
    });

    if (fs.length === 1) {
      return fs[0];
    }
    if (fs.length > 1) {
      return this.parseAND(fs);
    }
    return {};
  }

  // 合并过滤条件的值会存在this.filter内 obj是mongo过滤表达式
  public mergeFilter(
    ...p: {
      obj: object;
      s: V_filterLogical.AND | V_filterLogical.OR | V_filterLogical.NOR;
    }[]
  ) {
    p.forEach(f => {
      if (Object.keys(f.obj).length === 0) {
        throw new Error('不接受一个空的对象');
      }
      const isExists: boolean = Object.keys(this.filter).length > 0;

      if (f.s === V_filterLogical.AND) {
        if (isExists) {
          this.filter = this.parseAND([this.filter, f.obj]);
        } else {
          this.filter = f.obj;
        }
      } else if (f.s === V_filterLogical.OR) {
        if (isExists) {
          this.filter = this.parseOR([this.filter, f.obj]);
        } else {
          this.filter = f.obj;
        }
      } else if (f.s === V_filterLogical.NOR) {
        if (isExists) {
          this.filter = this.parseNOR([this.filter, f.obj]);
        } else {
          this.filter = f.obj;
        }
      }
    });
  }

  // 将{field value}[] 数组编译成 {$and:[{$and:[{f1:{$eq:v1}},{fn:{$eq:vn}}]}...]
  public parseFvs(...vs: I_fv[][]): object {
    const objs: Array<object> = [];
    vs.forEach(ele => {
      const v = this.parseWithFieldValue(ele);
      Object.keys(v).length > 0 && objs.push(v);
    });
    if (objs.length > 1) {
      return this.parseAND(objs);
    }
    if (objs.length === 0) {
      return {};
    }
    return objs[0];
  }

  // 编译字段选择条件 {$and:[{f1:{$in:[...]}},{f2:{$in:[...]}}]}
  public parseSelectFilter(
    p: Map<string, Set<string | number | null>>
  ): object {
    const objs: Array<object> = [];
    let len: number = 0;
    p.forEach((v, k) => {
      len++;
      const obj: object = {};
      obj[k] = { $in: Array.from(v) };
      objs.push(obj);
    });
    if (len === 0) {
      return {};
    }
    if (len === 1) {
      return objs[0];
    }
    return { $and: objs };
  }

  // obj是mongo过滤表达式
  private parseLoical(
    vs: Array<object>,
    s: V_filterLogical = V_filterLogical.AND |
      V_filterLogical.OR |
      V_filterLogical.NOR
  ): object {
    if (vs.length === 0) {
      return {};
    }
    if (vs.length > 1) {
      switch (s) {
        case V_filterLogical.AND:
          return { $and: vs };
        case V_filterLogical.OR:
          return { $or: vs };
        case V_filterLogical.NOR:
          return { $nor: vs };
        default:
          return {};
      }
    }
    return vs[0];
  }

  // 与 // obj是mongo过滤表达式
  public parseAND(vs: Array<object>): object {
    return this.parseLoical(vs, V_filterLogical.AND);
  }

  // 或// obj是mongo过滤表达式
  public parseOR(vs: Array<object>): object {
    return this.parseLoical(vs, V_filterLogical.OR);
  }

  // 或非// obj是mongo过滤表达式
  public parseNOR(vs: Array<object>): object {
    return this.parseLoical(vs, V_filterLogical.NOR);
  }

  // 等于
  public parseEQ(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $eq: this.formatValue(value) };
    }
    return obj;
  }

  // 不等于
  public parseNE(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $ne: this.formatValue(value) };
    }
    return obj;
  }

  // 大于
  public parseGT(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $gt: this.formatValue(value) };
    }
    return obj;
  }

  // 小于
  public parseLT(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $lt: this.formatValue(value) };
    }
    return obj;
  }

  // 大于等于
  public parseGTE(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $gte: this.formatValue(value) };
    }
    return obj;
  }

  // 小于等于
  public parseLTE(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $lte: this.formatValue(value) };
    }
    return obj;
  }

  // 取反
  public parseNOT(fieldName: string, value: any): object {
    const obj: object = {};
    if (fieldName && value) {
      obj[fieldName] = { $not: this.formatValue(value) };
    }
    return obj;
  }

  // 包含
  public parseIN(fieldName: string, vs: Array<string | number>): object {
    const obj: object = {};
    if (fieldName && vs.length === 0) {
      return {};
    }
    const vals: Array<string | number> = [];
    vs.forEach(v => {
      v && vals.push(this.formatValue(v));
    });
    obj[fieldName] = { $in: vals };
    return obj;
  }

  // 不包含
  public parseNIN(fieldName: string, vs: Array<string | number>): object {
    const obj: object = {};
    if (fieldName && vs.length === 0) {
      return {};
    }
    const vals: Array<string | number> = [];
    vs.forEach(v => {
      v && vals.push(this.formatValue(v));
    });
    obj[fieldName] = { $nin: vals };
    return obj;
  }
}

export interface I_filter extends T_filter {}
